﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace Parser
{
    /// <summary>
    /// Parses the xml instructions file, gets the raw text parsing information and instructions.
    /// </summary>
    class XmlParser
    {
        string _xml_file;
        XmlDocument _xmlDoc; //xml document object. 

        XDocument _xd; //LINQ 2 XML


        public XmlParser(string xml_file)
        {
            _xml_file = xml_file;
            try
            {
                XmlDocument xmlDoc = new XmlDocument(); //* create an xml document object. 
                xmlDoc.Load(_xml_file); //* load the XML document from the specified file. 
                //linq2xml
                _xd = XDocument.Load(_xml_file);

                _xmlDoc = xmlDoc;
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception occured");
                Console.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// Obsolete!!! use getFileNames!!!! 
        /// Returns the file names of the data source files.  
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> FileNames
        {
            get
            {
                try
                {
                    List<string> files_list = new List<string>();
                    XmlNodeList source_file_names = _xmlDoc.GetElementsByTagName("filename");
                    foreach (XmlNode node in source_file_names)
                        files_list.Add(node.InnerText);

                    return files_list;
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception occured");
                    Console.WriteLine(e.Message);
                    return null;
                }

            }
        }

        #region linear tree parsing
        /// <summary>
        /// Returns the separator characters of the data source file.  
        /// Currently  made obsolete by the LINQ2XML method.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> getSeparators(string file_node_id)
        {

            try
            {
                XmlNode node = findElementNode(file_node_id);
                XmlNode subnode = findNode("separators", node);
                List<string> separators = new List<string>();
                foreach (XmlNode separator in subnode.ChildNodes)
                    separators.Add(separator.InnerText);

                return separators;
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception occured");
                Console.WriteLine(e.Message);
                return null;
            }


        }

        /// <summary>
        /// finds an element that contains a node with matching text value.
        /// </summary>
        /// <param name="id">some text to find</param>
        /// <returns></returns>
        private XmlNode findElementNode(string id)
        {
            XmlNode node = _xmlDoc.DocumentElement.FirstChild;


            while (node != null)
            {
                XmlNodeList subnodes = node.ChildNodes;
                foreach (XmlNode subnode in subnodes)
                {
                    if (subnode.InnerText == id)
                    {
                        return node;
                    }
                }
                node = node.NextSibling;
            }
            return null;

        }

        /// <summary>
        /// finds a node with matching tag name in given element.
        /// </summary>
        /// <param name="tag_name"></param>
        /// <param name="element"></param>
        /// <returns></returns>
        private XmlNode findNode(string tag_name, XmlNode element)
        {


            foreach (XmlNode subnode in element.ChildNodes)
            {
                if (subnode.Name == tag_name)
                {
                    return subnode;
                }
            }

            return null;

        }

        #endregion

        #region using LINQ to XML

        /// <summary>
        /// Returns the file names of the data source files. 
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> getFileNames()
        {
            var n = from ff in _xd.Descendants("source_file") select ff.Element("filename").Value;
            return n;
            //return n.GetEnumerator();
        }

        /// <summary>
        /// finds and returns all column names for given file.
        /// </summary>
        /// <param name="file_name"></param>
        /// <returns></returns>
        public IEnumerable<string> getColumnNames(string file_name)
        {
            var n = from ff in _xd.Descendants("source_file")
                    where (ff.Element("filename").Value == file_name)
                    select ff.Element("columns");
            var k = from tt in n.Elements() select tt.Name.ToString();
            return k;
        }

        /// <summary>
        /// finds index of given column in given file.
        /// in case of not-found/error returns -1.
        /// </summary>
        /// <param name="file_name"></param>
        /// <param name="col_name"></param>
        /// <returns></returns>
        public int getColumnIndex(string file_name, string col_name)
        {
            var n = from ff in _xd.Descendants("source_file")
                    where (ff.Element("filename").Value == file_name)
                    select ff.Element("columns");
            var k = from tt in n.Elements()
                    where tt.Name == col_name
                    select tt.Value.ToString();

            if (k.Count() > 0)
                return Int32.Parse(k.First());
            else return -1;
        }


        /// <summary>
        /// works well 
        /// </summary>
        /// <param name="file_id"></param>
        /// <returns></returns>
        public IEnumerable<string> getSeparators2(string file_id)
        {
            var n = from ff in _xd.Descendants("source_file")
                    where (ff.Element("filename").Value == file_id)
                    select ff.Element("separators");
            var t = (from ss in n.Descendants() select ss.Value).ToArray();
            //removing the "" envelope

            for (int i = 0; i < t.Length; i++)
            {

                if (t[i].Length > 0)
                    t[i] = t[i].Substring(1, t[i].Length - 2);

            }

            return t.AsEnumerable();


        }



        #endregion
    }
}
